/**
 * Copyright (c) 2009 Cerner Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Cerner Corporation - initial API and implementation
 */
package com.cerner.system.rest.jaas.cas;

import java.net.URI;

import org.restlet.Client;
import org.restlet.data.Form;
import org.restlet.data.Response;
import org.restlet.data.Status;

/**
 * Internal HTTP-specific module that is responsible for the generation of CAS
 * service tickets for a single ticket granting ticket. Use an external
 * synchronization policy on it.
 * 
 * @author Alex Horn
 * @since 1.0.0
 */
class CasTicketConnector {

  /**
   * HTTP Connector used to {@code POST} to the {@link #casLoginUrl}.
   */
  private final Client client;

  /**
   * CAS 1.0 TGT.
   */
  private final String casTicketGrantingTicket;

  /**
   * URL of REST service that produces service tickets.
   */
  private final URI casLoginUrl;

  /**
   * Construct a new module that is able to create CAS service tickets.
   * 
   * @param client HTTP module that is responsible for the socket management
   *          when {@code POST}ing to {@code casLoginUrl}
   * @param casLoginUrl URL of the CAS context root with a trailing slash
   * @param casTicketGrantingTicket ticket granting ticket that allows CAS to
   *          act as a single sign-on server
   */
  CasTicketConnector(Client client, URI casLoginUrl,
      String casTicketGrantingTicket) {
    this.client = client;
    this.casLoginUrl = casLoginUrl;
    this.casTicketGrantingTicket = casTicketGrantingTicket;
  }

  /**
   * Obtain the URL of REST service that produces CAS service tickets.
   */
  URI getCasLoginUrl() {
    return this.casLoginUrl;
  }

  /**
   * Create a new CAS 1.0 service ticket for the provided service URI.
   * 
   * @throws CasConnectorException if the ticket granting ticket is invalid
   * @throws CasConnectorFault if an unrecoverable exception has occurred
   */
  String create(URI service) throws CasConnectorException, CasConnectorFault {
    assert service != null;

    final Response response;
    final Form form = new Form();
    form.add("service", service.toString());

    try {
      response = this.client.post(postUrl(), form.getWebRepresentation());
      if (response.getStatus().isSuccess()) {
        return response.getEntity().getText();
      }
    } catch (Exception e) {
      throw new CasConnectorFault(e);
    }

    if (Status.CLIENT_ERROR_NOT_FOUND.equals(response.getStatus())) {
      // http://jasig.princeton.edu/source/browse/jasigsvn/cas3/tags/cas-3-3-1-final/cas-server-integration-restlet/src/main/java/org/jasig/cas/integration/restlet/TicketGrantingTicketResource.java?r=43954#l71
      throw new CasConnectorException(
          "Unable to create CAS service ticket due to invalid ticket granting ticket");
    } else {
      throw new CasConnectorFault(response.getStatus());
    }
  }

  /**
   * Construct the URI to which {@code POST} requests result in a new CAS
   * service ticket.
   */
  String postUrl() {
    if (this.casLoginUrl.getPath().endsWith("/")) {
      return this.casLoginUrl.resolve(this.casTicketGrantingTicket).toString();
    } else {
      return postUrlUnsafe();
    }
  }

  /**
   * Strictly internal use only. Unsafe.
   * 
   * @return appended slash "/" to casLoginUrl and TGT
   */
  private String postUrlUnsafe() {
    return this.casLoginUrl.toString().concat("/").concat(
        this.casTicketGrantingTicket);
  }
}
